﻿using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using CID.Execution.Support.Scripting;
using NailsFramework.IoC;
using CID.Execution.Agent.Exceptions;
using System.Threading;

namespace CID.Execution.Agent
{
    [Lemming(Singleton = true)]
    public class AgentCoordinator : IAgentCoordinator
    {
        private static readonly TimeSpan TaskWaitTimeout = new TimeSpan(0, 5, 0);

        private BlockingCollection<IScriptExecuter> dispatchQueue;
        private Task mainTask;
        private ManualResetEvent synchronizer;
        
        public AgentCoordinator()
        {
        }

        public void Start()
        {
            if (!IsStarted)
            {
                synchronizer = new ManualResetEvent(false);
                dispatchQueue = new BlockingCollection<IScriptExecuter>();
                mainTask = Task.Factory.StartNew(ProcessScriptExecutionRequests);
                synchronizer.WaitOne();
            }
        }

        public void Stop()
        {
            if (IsStarted)
            {
                dispatchQueue.CompleteAdding();
                mainTask.Wait(TaskWaitTimeout);
                mainTask.Dispose();
                mainTask = null;
                dispatchQueue.Dispose();
                dispatchQueue = null;
                synchronizer.Dispose();
                synchronizer = null;
            }
        }

        public bool IsStarted { get { return ((mainTask != null) && (mainTask.Status == TaskStatus.Running)); } }

        public void Dispatch(LocationActivationScript script)
        {
            if (!IsStarted) throw new AgentCoordinatorNotStartedException("Dispatch Activation");
            dispatchQueue.Add(new ActivationScriptExecuter(script));
        }

        public void Dispatch(LocationDeactivationScript script)
        {
            if (!IsStarted) throw new AgentCoordinatorNotStartedException("Dispatch Deactivation");
            dispatchQueue.Add(new DeactivationScriptExecuter(script));
        }

        public void Dispatch(LocationDeploymentScript script)
        {
            if (!IsStarted) throw new AgentCoordinatorNotStartedException("Dispatch Deployment");
            dispatchQueue.Add(new DeploymentScriptExecuter(script));
        }

        private void ProcessScriptExecutionRequests()
        {
            synchronizer.Set();
            foreach (var executer in dispatchQueue.GetConsumingEnumerable())
                Task.Factory.StartNew(executer.Execute, TaskCreationOptions.AttachedToParent);
        }
    }
}
